library(tidyverse)
library(sf)
library(maps)
library(plotly)
library(leaflet)
library(htmltools)
library(tidytext)
library(textmineR)
library(ggraph)
library(wordcloud)
library(tm)
library(topicmodels)
Introduction
For this project I decided to create my own dataset based on information found here. I was interested to see the growth of Master’s Degrees in Data Science and Business Analytics all around the US. This dataset contains about 300 different schools. Some schools are listed multiple times because they offer an MS in Data Science or an MS in Business Analytics. There are 11 columns describing the university name, which school/department they are part of, the degree subject (A, BA, DA, DS), the year the degree was established, the location of the university, the degree description and the URL to their website. The four Degree subjects are Analytics (A), Business Analytics (BA), Data Analytics (DA), and Data Science (DS). Each part of this data set was used to explore the growth of Master’s Degrees in these subjects.
programs <- read.csv("../data/programsthroughoutyears.csv")
head(programs)
The Growth
Data science was named the fastest-growing job in 2017 by LinkedIn, and in 2018 Glassdoor ranked data scientist as the best job in the United States. Furthermore, a recent study by PriceWaterhouseCoopers states: “The best jobs right now in America include titles like data scientist, data engineer, and business analyst.”
The U.S. Bureau of Labor Statistics projects that employment in mathematical science and analytics careers will grow by 27.9% from 2016 to 2026. This is much faster than most other occupations and is expected to result in more than 50,000 new jobs. The average annual wage for data scientists and mathematical science occupations is $103,930.
Based on knowing this information, I was curious to see how much Master’s degrees have grown already and maybe what we can expect to see in the future.
dsba_per_year <- programs %>%
group_by(DS_or_BA, Year_Est) %>%
count(name = "Degrees_Per_Year")
dsba_per_year
dsbabar <- ggplot(dsba_per_year, aes(fill = DS_or_BA, y = Degrees_Per_Year, x = Year_Est,
text = paste("Degree Subject: ", DS_or_BA,"<br>New Degrees Created: ", Degrees_Per_Year, "<br>Year Est: ", Year_Est))) +
geom_bar(position = "stack", stat = "identity") +
scale_x_continuous(breaks = 2007:2021) +
labs(x = "",
y = "New MS Degrees by Year",
title = "The Growth of 4 MS Degrees Each Year",
fill = "Degree Subject") +
theme_minimal() +
scale_fill_manual(values = c("#E69F00", "#009E73", "#D55E00", "#56B4E9")) +
theme(plot.title.position = "plot",
plot.title = element_text(face = "bold"))
ggplotly(dsbabar, tooltip = "text")
NA
Subjects by the Numbers
dsba_nums <- programs %>%
group_by(DS_or_BA) %>%
count(name = "Amount of MS Degrees per Subject", sort = TRUE)
dsba_nums
We can see that throughout the years, each degree subject has increased. Based on this plot, we can definitely see the demand for these types of subjects. Towards 2019 and on, the schools offering MS degrees in these 4 subjects did decrease. The demand is still there but through some research, I learned that a lot of schools started offering Online Masters Degrees in these 4 subjects. Some say it is due to the pandemic, others say that people want more control over their schedule and where and when they take courses. Either way, based on this information, whether in-person or online - Analytics, Business Analytics, Data Analytics, and Data Science are high in demand and Universities everywhere are realizing how important it is and are creating Master’s degrees just for these subjects.
Graduate Degree Programs in Analytics and Data Science in the US
The next thing I wanted to look at was from this list of about 300 different universities, where they were located. I am assuming there is no pattern between states and the growth of degrees created throughout the years.
# Converting the dataframe to an sf object using the coordinates in the dataset.
universities_sf <- st_as_sf(programs, coords = c('Long','Lat'))
# Generates a dataframe of the states and sends that to the sf object created.
state_map_data <- map('state', fill = TRUE, plot = FALSE) %>%
st_as_sf()
# Sets the coordinate reference of the dataframe to match the coordinate reference of the States.
universities_sf <- st_set_crs(universities_sf, st_crs(state_map_data))
# Created a color palette for the map.
pal <- colorFactor(palette = c("#E69F00", "#009E73", "#D55E00", "#56B4E9"), domain = universities_sf$DS_or_BA)
Interactive Map to view the different MS Subjects in each state
Below I created an interactive map of about 300 different universities offering MS degrees in Analytics, Data Science, Business Analytics, and Data Analytics. You can hover over each point to view the name of the University. You can also click each point and that will give you the link to each Universities Degree page. You also have the option to view each degree subject by itself or all 4 together.
universities_sf <- universities_sf %>%
mutate(tag = paste0("Link to Degree Page: <a href=", URL, ">", URL, "</a>"))
leaflet() %>%
addTiles() %>%
addCircleMarkers(data = universities_sf, label = ~htmlEscape(University_Name), popup = ~tag,
fillColor = ~pal(DS_or_BA), fillOpacity = 1, stroke = FALSE, radius = 5,
group = universities_sf$DS_or_BA) %>%
addLayersControl(overlayGroups = universities_sf$DS_or_BA, options = layersControlOptions(collapsed = FALSE))
Universities offering Graduate Degree Programs in Analytics and Data Science
sf::sf_use_s2(FALSE)
Spherical geometry (s2) switched off
state_map_data$college_count <- lengths(st_intersects(state_map_data, universities_sf))
although coordinates are longitude/latitude, st_intersects assumes that they are planar
countmap <- ggplot() +
geom_sf(data = state_map_data, aes(fill = college_count, text = paste("State: ", ID, "<br>Count: ", college_count)), color = "black", size = 0.15) +
scale_fill_distiller(palette = "PuBu", direction = +1) +
labs(fill = NULL) +
theme_void() +
theme(plot.title = element_text(face = "bold")) +
labs(title = 'Universities Offering Graduate Degree Programs <br> in Analytics and Data Science')
Warning: Ignoring unknown aesthetics: text
ggplotly(countmap, tooltip = "text")
In this map we can see the map of the US and if you hover over each state you can see the amount of schools in each state with one of the four degrees we were exploring. In the future I would like to explore why some states have a much higher count of universities offering these MS degrees. Most likely those states have much more universities or it could be based on the population of each state.
LDA Topic Modeling for the Degree Description for each University
The final thing I wanted to look at was to see if there were common topics between the degree descriptions for the 4 different degree subjects. It could be that some of the topics are more Computer Science based or more Statistics based degrees.
I did this by just looking at the Des_Desc column in the Programs dataset I have been using. I turned this data into a corpus which is just a format for storing textual data. I then ran 5 different transformations to prepare the data for analysis. I then converted the dfCorpus to a document term matrix in order to use LDA. I created a four-topic LDA model to see how words are associated with topics and how topics are associated with documents.I specifically used the beta model which computes the probability of a term being generated from a topic. I specifically wanted to visualize the top 20 terms that are most common within each topic.
dfCorpus <- SimpleCorpus(VectorSource(programs$Deg_Desc))
# Stripping any extra white space:
dfCorpus <- tm_map(dfCorpus, stripWhitespace)
# Transforming everything to lowercase
dfCorpus <- tm_map(dfCorpus, content_transformer(tolower))
# Removing numbers
dfCorpus <- tm_map(dfCorpus, removeNumbers)
# Removing punctuation
dfCorpus <- tm_map(dfCorpus, removePunctuation)
# Removing stop words
dfCorpus <- tm_map(dfCorpus, removeWords, stopwords("english"))
DTM <- DocumentTermMatrix(dfCorpus)
des_lda <- LDA(DTM, k = 4, control = list(seed = 2021))
des_topics <- tidy(des_lda, matrix = "beta")
top_terms <- des_topics %>%
group_by(topic) %>%
top_n(20, beta) %>%
ungroup() %>%
arrange(topic, -beta)
LDA Visualization
top_terms %>%
mutate(term = reorder(term, beta)) %>%
ggplot(aes(x = term, y = beta, fill = factor(topic))) +
geom_col(show.legend = FALSE) +
scale_fill_manual(values = c("#E69F00", "#009E73", "#D55E00", "#56B4E9")) +
facet_wrap(~ topic, scales = "free") +
labs(title = "Topic modeling in MS in Data Science Degree Descriptions", subtitle = "Latent Dirichlet Allocation", x = "") +
coord_flip() +
theme_minimal() +
theme(plot.title.position = "plot",
plot.title = element_text(face = "bold"))

Based on this four-topic LDA, we see that “data”, “science”, and “business” would most likely appear in any topics in the future. We can also see that some topics mention statistics, others mention analysis, and some mention machine (learning) and big (data). Based on this information, I think if someone looked at this LDA, they would be able to tell that this is something school related and describing a Master’s Degree Description of a data related subject.
Word Cloud of Most Common Words
Part of exploring these terms, I wanted to include this last visualization that visualizes the most common words and changes the size of the terms based on how often they were used. Although it is kind of obvious, “data”, “science”, “analytics”, “business”, and “program” are the most common and most used words in the degree descriptions which perfectly summarizes the 4 degree subjects I discovered throughout the project.
set.seed(2021)
sums <- as.data.frame(colSums(as.matrix(DTM)))
sums <- rownames_to_column(sums)
colnames(sums) <- c("term", "count")
sums <- arrange(sums, desc(count))
head <- sums[1:99,]
wordcloud(words = head$term, freq = head$count,
max.words=100, random.order=FALSE, rot.per=0.35,
colors= c("#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7"))

Conclusion
Throughout this project, I was able to discuss different Masters Degree subjects in different universities throughout the US. I was able to show how the amount of degrees that universities have grown since 2007. I was also able to create an interactive map to view the different schools and that can take you to the school’s website if someone wanted more information about the school and its specific department. The final thing I was able to show was how to potentially predict future degree descriptions using the most common words found.
Since I created my own dataset, I was able to include all the necessary information needed to complete this project and did not have a lot of cleaning and preparing. Most of the cleaning happened when I created the LDA model since the data needs to be tidy and needs to be one-token-per-row table in order to actually perform LDA.
Every chart I planned on creating was made for this project.The most difficult part of this project was using leaflet() to create one of my maps and getting the layer option to connect with the actual data points. I am happy we were able to figure this out because I think it allows people to look at this information and it not be overwhelming. The other difficult part I had with this project was LDA Topic Modeling. I knew I wanted to do this but had trouble actually understanding what it was doing.
In terms of principles of data visualization, I tried keeping everything minimal. I wanted all the visualizations to have a fixed size so that the interactive part of the plots would work smoothly no matter the size of the screen. I also used colors that are color blind friendly. Like the past project, I made sure to align the title of the graph skewed to the left.
LS0tDQp0aXRsZTogIkRhdGEgVmlzdWFsaXphdGlvbiAtIE1pbmktUHJvamVjdCAyIg0KYXV0aG9yOiAiT3JlbCBZb3NoaWEgYG95b3NoaWEzOTcyQGZsb3JpZGFwb2x5LmVkdWAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQogICAgdG9jX2RlcHRoOiA0DQogICAgbnVtYmVyX3NlY3Rpb25zOiBUUlVFDQogICAgdGhlbWU6IGNvc21vDQotLS0NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShzZikNCmxpYnJhcnkobWFwcykNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShsZWFmbGV0KQ0KbGlicmFyeShodG1sdG9vbHMpDQpsaWJyYXJ5KHRpZHl0ZXh0KQ0KbGlicmFyeSh0ZXh0bWluZVIpDQpsaWJyYXJ5KGdncmFwaCkNCmxpYnJhcnkod29yZGNsb3VkKQ0KbGlicmFyeSh0bSkNCmxpYnJhcnkodG9waWNtb2RlbHMpDQpgYGANCg0KDQojIEludHJvZHVjdGlvbg0KDQpGb3IgdGhpcyBwcm9qZWN0IEkgZGVjaWRlZCB0byBjcmVhdGUgbXkgb3duIGRhdGFzZXQgYmFzZWQgb24gaW5mb3JtYXRpb24gZm91bmQgW2hlcmVdKGh0dHBzOi8vYW5hbHl0aWNzLm5jc3UuZWR1Lz9wYWdlX2lkPTQxODQpLiBJIHdhcyBpbnRlcmVzdGVkIHRvIHNlZSB0aGUgZ3Jvd3RoIG9mIE1hc3RlcidzIERlZ3JlZXMgaW4gRGF0YSBTY2llbmNlIGFuZCBCdXNpbmVzcyBBbmFseXRpY3MgYWxsIGFyb3VuZCB0aGUgVVMuIFRoaXMgZGF0YXNldCBjb250YWlucyBhYm91dCAzMDAgZGlmZmVyZW50IHNjaG9vbHMuIFNvbWUgc2Nob29scyBhcmUgbGlzdGVkIG11bHRpcGxlIHRpbWVzIGJlY2F1c2UgdGhleSBvZmZlciBhbiBNUyBpbiBEYXRhIFNjaWVuY2Ugb3IgYW4gTVMgaW4gQnVzaW5lc3MgQW5hbHl0aWNzLiBUaGVyZSBhcmUgMTEgY29sdW1ucyBkZXNjcmliaW5nIHRoZSB1bml2ZXJzaXR5IG5hbWUsIHdoaWNoIHNjaG9vbC9kZXBhcnRtZW50IHRoZXkgYXJlIHBhcnQgb2YsIHRoZSBkZWdyZWUgc3ViamVjdCAoQSwgQkEsIERBLCBEUyksIHRoZSB5ZWFyIHRoZSBkZWdyZWUgd2FzIGVzdGFibGlzaGVkLCB0aGUgbG9jYXRpb24gb2YgdGhlIHVuaXZlcnNpdHksIHRoZSBkZWdyZWUgZGVzY3JpcHRpb24gYW5kIHRoZSBVUkwgdG8gdGhlaXIgd2Vic2l0ZS4gVGhlIGZvdXIgRGVncmVlIHN1YmplY3RzIGFyZSBBbmFseXRpY3MgKEEpLCBCdXNpbmVzcyBBbmFseXRpY3MgKEJBKSwgRGF0YSBBbmFseXRpY3MgKERBKSwgYW5kIERhdGEgU2NpZW5jZSAoRFMpLiBFYWNoIHBhcnQgb2YgdGhpcyBkYXRhIHNldCB3YXMgdXNlZCB0byBleHBsb3JlIHRoZSBncm93dGggb2YgTWFzdGVyJ3MgRGVncmVlcyBpbiB0aGVzZSBzdWJqZWN0cy4NCg0KYGBge3J9DQpwcm9ncmFtcyA8LSByZWFkLmNzdigiLi4vZGF0YS9wcm9ncmFtc3Rocm91Z2hvdXR5ZWFycy5jc3YiKQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChwcm9ncmFtcykNCmBgYA0KDQojIFRoZSBHcm93dGgNCg0KRGF0YSBzY2llbmNlIHdhcyBuYW1lZCB0aGUgZmFzdGVzdC1ncm93aW5nIGpvYiBpbiAyMDE3IGJ5IExpbmtlZEluLCBhbmQgaW4gMjAxOCBHbGFzc2Rvb3IgcmFua2VkIGRhdGEgc2NpZW50aXN0IGFzIHRoZSBiZXN0IGpvYiBpbiB0aGUgVW5pdGVkIFN0YXRlcy4gRnVydGhlcm1vcmUsIGEgcmVjZW50IHN0dWR5IGJ5IFByaWNlV2F0ZXJob3VzZUNvb3BlcnMgc3RhdGVzOiDigJxUaGUgYmVzdCBqb2JzIHJpZ2h0IG5vdyBpbiBBbWVyaWNhIGluY2x1ZGUgdGl0bGVzIGxpa2UgZGF0YSBzY2llbnRpc3QsIGRhdGEgZW5naW5lZXIsIGFuZCBidXNpbmVzcyBhbmFseXN0LuKAnQ0KDQpbVGhlIFUuUy4gQnVyZWF1IG9mIExhYm9yIFN0YXRpc3RpY3NdKGh0dHBzOi8vd3d3LmJscy5nb3Yvb3B1Yi9idG4vdm9sdW1lLTcvYmlnLWRhdGEtYWRkcy11cC5odG0pIHByb2plY3RzIHRoYXQgZW1wbG95bWVudCBpbiBtYXRoZW1hdGljYWwgc2NpZW5jZSBhbmQgYW5hbHl0aWNzIGNhcmVlcnMgd2lsbCBncm93IGJ5IDI3LjklIGZyb20gMjAxNiB0byAyMDI2LiBUaGlzIGlzIG11Y2ggZmFzdGVyIHRoYW4gbW9zdCBvdGhlciBvY2N1cGF0aW9ucyBhbmQgaXMgZXhwZWN0ZWQgdG8gcmVzdWx0IGluIG1vcmUgdGhhbiA1MCwwMDAgbmV3IGpvYnMuIFRoZSBhdmVyYWdlIGFubnVhbCB3YWdlIGZvciBkYXRhIHNjaWVudGlzdHMgYW5kIG1hdGhlbWF0aWNhbCBzY2llbmNlIG9jY3VwYXRpb25zIGlzICQxMDMsOTMwLg0KDQpCYXNlZCBvbiBrbm93aW5nIHRoaXMgaW5mb3JtYXRpb24sIEkgd2FzIGN1cmlvdXMgdG8gc2VlIGhvdyBtdWNoIE1hc3RlcidzIGRlZ3JlZXMgaGF2ZSBncm93biBhbHJlYWR5IGFuZCBtYXliZSB3aGF0IHdlIGNhbiBleHBlY3QgdG8gc2VlIGluIHRoZSBmdXR1cmUuDQoNCmBgYHtyfQ0KZHNiYV9wZXJfeWVhciA8LSBwcm9ncmFtcyAlPiUgDQogIGdyb3VwX2J5KERTX29yX0JBLCBZZWFyX0VzdCkgJT4lIA0KICBjb3VudChuYW1lID0gIkRlZ3JlZXNfUGVyX1llYXIiKSANCg0KZHNiYV9wZXJfeWVhcg0KYGBgDQoNCg0KYGBge3IgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9OX0NCmRzYmFiYXIgPC0gZ2dwbG90KGRzYmFfcGVyX3llYXIsIGFlcyhmaWxsID0gRFNfb3JfQkEsIHkgPSBEZWdyZWVzX1Blcl9ZZWFyLCB4ID0gWWVhcl9Fc3QsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZSgiRGVncmVlIFN1YmplY3Q6ICIsIERTX29yX0JBLCI8YnI+TmV3IERlZ3JlZXMgQ3JlYXRlZDogIiwgRGVncmVlc19QZXJfWWVhciwgIjxicj5ZZWFyIEVzdDogIiwgWWVhcl9Fc3QpKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAyMDA3OjIwMjEpICsNCiAgbGFicyh4ID0gIiIsDQogICAgICAgeSA9ICJOZXcgTVMgRGVncmVlcyBieSBZZWFyIiwNCiAgICAgICB0aXRsZSA9ICJUaGUgR3Jvd3RoIG9mIDQgTVMgRGVncmVlcyBFYWNoIFllYXIiLA0KICAgICAgIGZpbGwgPSAiRGVncmVlIFN1YmplY3QiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNFNjlGMDAiLCAiIzAwOUU3MyIsICIjRDU1RTAwIiwgIiM1NkI0RTkiKSkgKw0KICB0aGVtZShwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLCANCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSkgDQogIA0KDQpnZ3Bsb3RseShkc2JhYmFyLCB0b29sdGlwID0gInRleHQiKQ0KICANCmBgYA0KDQojIyBTdWJqZWN0cyBieSB0aGUgTnVtYmVycw0KDQpgYGB7cn0NCmRzYmFfbnVtcyA8LSBwcm9ncmFtcyAlPiUgDQogIGdyb3VwX2J5KERTX29yX0JBKSAlPiUgDQogIGNvdW50KG5hbWUgPSAiQW1vdW50IG9mIE1TIERlZ3JlZXMgcGVyIFN1YmplY3QiLCBzb3J0ID0gVFJVRSkNCg0KZHNiYV9udW1zDQpgYGANCg0KV2UgY2FuIHNlZSB0aGF0IHRocm91Z2hvdXQgdGhlIHllYXJzLCBlYWNoIGRlZ3JlZSBzdWJqZWN0IGhhcyBpbmNyZWFzZWQuIEJhc2VkIG9uIHRoaXMgcGxvdCwgd2UgY2FuIGRlZmluaXRlbHkgc2VlIHRoZSBkZW1hbmQgZm9yIHRoZXNlIHR5cGVzIG9mIHN1YmplY3RzLiBUb3dhcmRzIDIwMTkgYW5kIG9uLCB0aGUgc2Nob29scyBvZmZlcmluZyBNUyBkZWdyZWVzIGluIHRoZXNlIDQgc3ViamVjdHMgZGlkIGRlY3JlYXNlLiBUaGUgZGVtYW5kIGlzIHN0aWxsIHRoZXJlIGJ1dCB0aHJvdWdoIHNvbWUgcmVzZWFyY2gsIEkgbGVhcm5lZCB0aGF0IGEgbG90IG9mIHNjaG9vbHMgc3RhcnRlZCBvZmZlcmluZyAqKk9ubGluZSoqIE1hc3RlcnMgRGVncmVlcyBpbiB0aGVzZSA0IHN1YmplY3RzLiBTb21lIHNheSBpdCBpcyBkdWUgdG8gdGhlIHBhbmRlbWljLCBvdGhlcnMgc2F5IHRoYXQgcGVvcGxlIHdhbnQgbW9yZSBjb250cm9sIG92ZXIgdGhlaXIgc2NoZWR1bGUgYW5kIHdoZXJlIGFuZCB3aGVuIHRoZXkgdGFrZSBjb3Vyc2VzLiBFaXRoZXIgd2F5LCBiYXNlZCBvbiB0aGlzIGluZm9ybWF0aW9uLCB3aGV0aGVyIGluLXBlcnNvbiBvciBvbmxpbmUgLSBBbmFseXRpY3MsIEJ1c2luZXNzIEFuYWx5dGljcywgRGF0YSBBbmFseXRpY3MsIGFuZCBEYXRhIFNjaWVuY2UgYXJlIGhpZ2ggaW4gZGVtYW5kIGFuZCBVbml2ZXJzaXRpZXMgZXZlcnl3aGVyZSBhcmUgcmVhbGl6aW5nIGhvdyBpbXBvcnRhbnQgaXQgaXMgYW5kIGFyZSBjcmVhdGluZyBNYXN0ZXIncyBkZWdyZWVzIGp1c3QgZm9yIHRoZXNlIHN1YmplY3RzLg0KDQoNCiMgR3JhZHVhdGUgRGVncmVlIFByb2dyYW1zIGluIEFuYWx5dGljcyBhbmQgRGF0YSBTY2llbmNlIGluIHRoZSBVUw0KDQpUaGUgbmV4dCB0aGluZyBJIHdhbnRlZCB0byBsb29rIGF0IHdhcyBmcm9tIHRoaXMgbGlzdCBvZiBhYm91dCAzMDAgZGlmZmVyZW50IHVuaXZlcnNpdGllcywgd2hlcmUgdGhleSB3ZXJlIGxvY2F0ZWQuIEkgYW0gYXNzdW1pbmcgdGhlcmUgaXMgbm8gcGF0dGVybiBiZXR3ZWVuIHN0YXRlcyBhbmQgdGhlIGdyb3d0aCBvZiBkZWdyZWVzIGNyZWF0ZWQgdGhyb3VnaG91dCB0aGUgeWVhcnMuDQoNCmBgYHtyfQ0KIyBDb252ZXJ0aW5nIHRoZSBkYXRhZnJhbWUgdG8gYW4gc2Ygb2JqZWN0IHVzaW5nIHRoZSBjb29yZGluYXRlcyBpbiB0aGUgZGF0YXNldC4NCnVuaXZlcnNpdGllc19zZiA8LSBzdF9hc19zZihwcm9ncmFtcywgY29vcmRzID0gYygnTG9uZycsJ0xhdCcpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBHZW5lcmF0ZXMgYSBkYXRhZnJhbWUgb2YgdGhlIHN0YXRlcyBhbmQgc2VuZHMgdGhhdCB0byB0aGUgc2Ygb2JqZWN0IGNyZWF0ZWQuDQpzdGF0ZV9tYXBfZGF0YSA8LSBtYXAoJ3N0YXRlJywgZmlsbCA9IFRSVUUsIHBsb3QgPSBGQUxTRSkgJT4lDQogIHN0X2FzX3NmKCkNCmBgYA0KDQpgYGB7cn0NCiMgU2V0cyB0aGUgY29vcmRpbmF0ZSByZWZlcmVuY2Ugb2YgdGhlIGRhdGFmcmFtZSB0byBtYXRjaCB0aGUgY29vcmRpbmF0ZSByZWZlcmVuY2Ugb2YgdGhlIFN0YXRlcy4NCnVuaXZlcnNpdGllc19zZiA8LSBzdF9zZXRfY3JzKHVuaXZlcnNpdGllc19zZiwgc3RfY3JzKHN0YXRlX21hcF9kYXRhKSkNCmBgYA0KDQpgYGB7cn0NCiMgQ3JlYXRlZCBhIGNvbG9yIHBhbGV0dGUgZm9yIHRoZSBtYXAuDQpwYWwgPC0gY29sb3JGYWN0b3IocGFsZXR0ZSA9IGMoIiNFNjlGMDAiLCAiIzAwOUU3MyIsICIjRDU1RTAwIiwgIiM1NkI0RTkiKSwgZG9tYWluID0gdW5pdmVyc2l0aWVzX3NmJERTX29yX0JBKQ0KYGBgDQoNCiMjIEludGVyYWN0aXZlIE1hcCB0byB2aWV3IHRoZSBkaWZmZXJlbnQgTVMgU3ViamVjdHMgaW4gZWFjaCBzdGF0ZQ0KDQpCZWxvdyBJIGNyZWF0ZWQgYW4gaW50ZXJhY3RpdmUgbWFwIG9mIGFib3V0IDMwMCBkaWZmZXJlbnQgdW5pdmVyc2l0aWVzIG9mZmVyaW5nIE1TIGRlZ3JlZXMgaW4gQW5hbHl0aWNzLCBEYXRhIFNjaWVuY2UsIEJ1c2luZXNzIEFuYWx5dGljcywgYW5kIERhdGEgQW5hbHl0aWNzLiBZb3UgY2FuIGhvdmVyIG92ZXIgZWFjaCBwb2ludCB0byB2aWV3IHRoZSBuYW1lIG9mIHRoZSBVbml2ZXJzaXR5LiBZb3UgY2FuIGFsc28gY2xpY2sgZWFjaCBwb2ludCBhbmQgdGhhdCB3aWxsIGdpdmUgeW91IHRoZSBsaW5rIHRvIGVhY2ggVW5pdmVyc2l0aWVzIERlZ3JlZSBwYWdlLiBZb3UgYWxzbyBoYXZlIHRoZSBvcHRpb24gdG8gdmlldyBlYWNoIGRlZ3JlZSBzdWJqZWN0IGJ5IGl0c2VsZiBvciBhbGwgNCB0b2dldGhlci4gDQoNCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTl9DQp1bml2ZXJzaXRpZXNfc2YgPC0gdW5pdmVyc2l0aWVzX3NmICU+JSANCiAgbXV0YXRlKHRhZyA9IHBhc3RlMCgiTGluayB0byBEZWdyZWUgUGFnZTogPGEgaHJlZj0iLCBVUkwsICI+IiwgVVJMLCAiPC9hPiIpKQ0KDQpsZWFmbGV0KCkgJT4lIA0KICBhZGRUaWxlcygpICU+JSANCiAgYWRkQ2lyY2xlTWFya2VycyhkYXRhID0gdW5pdmVyc2l0aWVzX3NmLCBsYWJlbCA9IH5odG1sRXNjYXBlKFVuaXZlcnNpdHlfTmFtZSksIHBvcHVwID0gfnRhZywgIA0KICAgICAgICAgICAgICAgICAgIGZpbGxDb2xvciA9IH5wYWwoRFNfb3JfQkEpLCBmaWxsT3BhY2l0eSA9IDEsIHN0cm9rZSA9IEZBTFNFLCByYWRpdXMgPSA1LCANCiAgICAgICAgICAgICAgICAgICBncm91cCA9IHVuaXZlcnNpdGllc19zZiREU19vcl9CQSkgJT4lDQogIGFkZExheWVyc0NvbnRyb2wob3ZlcmxheUdyb3VwcyA9IHVuaXZlcnNpdGllc19zZiREU19vcl9CQSwgb3B0aW9ucyA9IGxheWVyc0NvbnRyb2xPcHRpb25zKGNvbGxhcHNlZCA9IEZBTFNFKSkNCmBgYA0KDQoNCiMjIFVuaXZlcnNpdGllcyBvZmZlcmluZyBHcmFkdWF0ZSBEZWdyZWUgUHJvZ3JhbXMgaW4gQW5hbHl0aWNzIGFuZCBEYXRhIFNjaWVuY2UNCg0KYGBge3J9DQpzZjo6c2ZfdXNlX3MyKEZBTFNFKQ0Kc3RhdGVfbWFwX2RhdGEkY29sbGVnZV9jb3VudCA8LSBsZW5ndGhzKHN0X2ludGVyc2VjdHMoc3RhdGVfbWFwX2RhdGEsIHVuaXZlcnNpdGllc19zZikpDQpgYGANCg0KYGBge3IgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9OX0NCmNvdW50bWFwIDwtIGdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gc3RhdGVfbWFwX2RhdGEsIGFlcyhmaWxsID0gY29sbGVnZV9jb3VudCwgdGV4dCA9IHBhc3RlKCJTdGF0ZTogIiwgSUQsICI8YnI+Q291bnQ6ICIsIGNvbGxlZ2VfY291bnQpKSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC4xNSkgKw0KICBzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlID0gIlB1QnUiLCBkaXJlY3Rpb24gPSArMSkgKw0KICBsYWJzKGZpbGwgPSBOVUxMKSArDQogIHRoZW1lX3ZvaWQoKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIikpICsNCiAgbGFicyh0aXRsZSA9ICdVbml2ZXJzaXRpZXMgT2ZmZXJpbmcgR3JhZHVhdGUgRGVncmVlIFByb2dyYW1zIDxicj4gaW4gQW5hbHl0aWNzIGFuZCBEYXRhIFNjaWVuY2UnKQ0KDQpnZ3Bsb3RseShjb3VudG1hcCwgdG9vbHRpcCA9ICJ0ZXh0IikNCmBgYA0KDQpJbiB0aGlzIG1hcCB3ZSBjYW4gc2VlIHRoZSBtYXAgb2YgdGhlIFVTIGFuZCBpZiB5b3UgaG92ZXIgb3ZlciBlYWNoIHN0YXRlIHlvdSBjYW4gc2VlIHRoZSBhbW91bnQgb2Ygc2Nob29scyBpbiBlYWNoIHN0YXRlIHdpdGggb25lIG9mIHRoZSBmb3VyIGRlZ3JlZXMgd2Ugd2VyZSBleHBsb3JpbmcuIEluIHRoZSBmdXR1cmUgSSB3b3VsZCBsaWtlIHRvIGV4cGxvcmUgd2h5IHNvbWUgc3RhdGVzIGhhdmUgYSBtdWNoIGhpZ2hlciBjb3VudCBvZiB1bml2ZXJzaXRpZXMgb2ZmZXJpbmcgdGhlc2UgTVMgZGVncmVlcy4gTW9zdCBsaWtlbHkgdGhvc2Ugc3RhdGVzIGhhdmUgbXVjaCBtb3JlIHVuaXZlcnNpdGllcyBvciBpdCBjb3VsZCBiZSBiYXNlZCBvbiB0aGUgcG9wdWxhdGlvbiBvZiBlYWNoIHN0YXRlLg0KDQojIExEQSBUb3BpYyBNb2RlbGluZyBmb3IgdGhlIERlZ3JlZSBEZXNjcmlwdGlvbiBmb3IgZWFjaCBVbml2ZXJzaXR5DQoNClRoZSBmaW5hbCB0aGluZyBJIHdhbnRlZCB0byBsb29rIGF0IHdhcyB0byBzZWUgaWYgdGhlcmUgd2VyZSBjb21tb24gdG9waWNzIGJldHdlZW4gdGhlIGRlZ3JlZSBkZXNjcmlwdGlvbnMgZm9yIHRoZSA0IGRpZmZlcmVudCBkZWdyZWUgc3ViamVjdHMuIEl0IGNvdWxkIGJlIHRoYXQgc29tZSBvZiB0aGUgdG9waWNzIGFyZSBtb3JlIENvbXB1dGVyIFNjaWVuY2UgYmFzZWQgb3IgbW9yZSBTdGF0aXN0aWNzIGJhc2VkIGRlZ3JlZXMuIA0KDQpJIGRpZCB0aGlzIGJ5IGp1c3QgbG9va2luZyBhdCB0aGUgRGVzX0Rlc2MgY29sdW1uIGluIHRoZSBQcm9ncmFtcyBkYXRhc2V0IEkgaGF2ZSBiZWVuIHVzaW5nLiBJIHR1cm5lZCB0aGlzIGRhdGEgaW50byBhIGNvcnB1cyB3aGljaCBpcyBqdXN0IGEgZm9ybWF0IGZvciBzdG9yaW5nIHRleHR1YWwgZGF0YS4gSSB0aGVuIHJhbiA1IGRpZmZlcmVudCB0cmFuc2Zvcm1hdGlvbnMgdG8gcHJlcGFyZSB0aGUgZGF0YSBmb3IgYW5hbHlzaXMuIEkgdGhlbiBjb252ZXJ0ZWQgdGhlIGRmQ29ycHVzIHRvIGEgZG9jdW1lbnQgdGVybSBtYXRyaXggaW4gb3JkZXIgdG8gdXNlIExEQS4gSSBjcmVhdGVkIGEgZm91ci10b3BpYyBMREEgbW9kZWwgdG8gc2VlIGhvdyB3b3JkcyBhcmUgYXNzb2NpYXRlZCB3aXRoIHRvcGljcyBhbmQgaG93IHRvcGljcyBhcmUgYXNzb2NpYXRlZCB3aXRoIGRvY3VtZW50cy5JIHNwZWNpZmljYWxseSB1c2VkIHRoZSBiZXRhIG1vZGVsIHdoaWNoIGNvbXB1dGVzIHRoZSBwcm9iYWJpbGl0eSBvZiBhIHRlcm0gYmVpbmcgZ2VuZXJhdGVkIGZyb20gYSB0b3BpYy4gSSBzcGVjaWZpY2FsbHkgd2FudGVkIHRvIHZpc3VhbGl6ZSB0aGUgdG9wIDIwIHRlcm1zIHRoYXQgYXJlIG1vc3QgY29tbW9uIHdpdGhpbiBlYWNoIHRvcGljLg0KDQpgYGB7cn0NCmRmQ29ycHVzIDwtIFNpbXBsZUNvcnB1cyhWZWN0b3JTb3VyY2UocHJvZ3JhbXMkRGVnX0Rlc2MpKQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIFN0cmlwcGluZyBhbnkgZXh0cmEgd2hpdGUgc3BhY2U6DQpkZkNvcnB1cyA8LSB0bV9tYXAoZGZDb3JwdXMsIHN0cmlwV2hpdGVzcGFjZSkNCg0KIyBUcmFuc2Zvcm1pbmcgZXZlcnl0aGluZyB0byBsb3dlcmNhc2UNCmRmQ29ycHVzIDwtIHRtX21hcChkZkNvcnB1cywgY29udGVudF90cmFuc2Zvcm1lcih0b2xvd2VyKSkNCg0KIyBSZW1vdmluZyBudW1iZXJzIA0KZGZDb3JwdXMgPC0gdG1fbWFwKGRmQ29ycHVzLCByZW1vdmVOdW1iZXJzKQ0KDQojIFJlbW92aW5nIHB1bmN0dWF0aW9uDQpkZkNvcnB1cyA8LSB0bV9tYXAoZGZDb3JwdXMsIHJlbW92ZVB1bmN0dWF0aW9uKQ0KDQojIFJlbW92aW5nIHN0b3Agd29yZHMNCmRmQ29ycHVzIDwtIHRtX21hcChkZkNvcnB1cywgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygiZW5nbGlzaCIpKQ0KYGBgDQoNCmBgYHtyfQ0KRFRNIDwtIERvY3VtZW50VGVybU1hdHJpeChkZkNvcnB1cykNCmBgYA0KDQpgYGB7cn0NCmRlc19sZGEgPC0gTERBKERUTSwgayA9IDQsIGNvbnRyb2wgPSBsaXN0KHNlZWQgPSAyMDIxKSkNCmBgYA0KDQpgYGB7cn0NCmRlc190b3BpY3MgPC0gdGlkeShkZXNfbGRhLCBtYXRyaXggPSAiYmV0YSIpDQpgYGANCg0KYGBge3J9DQp0b3BfdGVybXMgPC0gZGVzX3RvcGljcyAlPiUgDQogIGdyb3VwX2J5KHRvcGljKSAlPiUgDQogIHRvcF9uKDIwLCBiZXRhKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIGFycmFuZ2UodG9waWMsIC1iZXRhKQ0KYGBgDQoNCiMjIExEQSBWaXN1YWxpemF0aW9uDQoNCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTl9DQp0b3BfdGVybXMgJT4lDQogIG11dGF0ZSh0ZXJtID0gcmVvcmRlcih0ZXJtLCBiZXRhKSkgJT4lDQogIGdncGxvdChhZXMoeCA9IHRlcm0sIHkgPSBiZXRhLCBmaWxsID0gZmFjdG9yKHRvcGljKSkpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjRTY5RjAwIiwgIiMwMDlFNzMiLCAiI0Q1NUUwMCIsICIjNTZCNEU5IikpICsNCiAgZmFjZXRfd3JhcCh+IHRvcGljLCBzY2FsZXMgPSAiZnJlZSIpICsNCiAgbGFicyh0aXRsZSA9ICJUb3BpYyBtb2RlbGluZyBpbiBNUyBpbiBEYXRhIFNjaWVuY2UgRGVncmVlIERlc2NyaXB0aW9ucyIsIHN1YnRpdGxlID0gIkxhdGVudCBEaXJpY2hsZXQgQWxsb2NhdGlvbiIsIHggPSAiIikgKw0KICBjb29yZF9mbGlwKCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLCANCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSkNCmBgYA0KDQpCYXNlZCBvbiB0aGlzIGZvdXItdG9waWMgTERBLCB3ZSBzZWUgdGhhdCAiZGF0YSIsICJzY2llbmNlIiwgYW5kICJidXNpbmVzcyIgd291bGQgbW9zdCBsaWtlbHkgYXBwZWFyIGluIGFueSB0b3BpY3MgaW4gdGhlIGZ1dHVyZS4gV2UgY2FuIGFsc28gc2VlIHRoYXQgc29tZSB0b3BpY3MgbWVudGlvbiBzdGF0aXN0aWNzLCBvdGhlcnMgbWVudGlvbiBhbmFseXNpcywgYW5kIHNvbWUgbWVudGlvbiBtYWNoaW5lIChsZWFybmluZykgYW5kIGJpZyAoZGF0YSkuIEJhc2VkIG9uIHRoaXMgaW5mb3JtYXRpb24sIEkgdGhpbmsgaWYgc29tZW9uZSBsb29rZWQgYXQgdGhpcyBMREEsIHRoZXkgd291bGQgYmUgYWJsZSB0byB0ZWxsIHRoYXQgdGhpcyBpcyBzb21ldGhpbmcgc2Nob29sIHJlbGF0ZWQgYW5kIGRlc2NyaWJpbmcgYSBNYXN0ZXIncyBEZWdyZWUgRGVzY3JpcHRpb24gb2YgYSBkYXRhIHJlbGF0ZWQgc3ViamVjdC4NCg0KIyMgV29yZCBDbG91ZCBvZiBNb3N0IENvbW1vbiBXb3Jkcw0KDQpQYXJ0IG9mIGV4cGxvcmluZyB0aGVzZSB0ZXJtcywgSSB3YW50ZWQgdG8gaW5jbHVkZSB0aGlzIGxhc3QgdmlzdWFsaXphdGlvbiB0aGF0IHZpc3VhbGl6ZXMgdGhlIG1vc3QgY29tbW9uIHdvcmRzIGFuZCBjaGFuZ2VzIHRoZSBzaXplIG9mIHRoZSB0ZXJtcyBiYXNlZCBvbiBob3cgb2Z0ZW4gdGhleSB3ZXJlIHVzZWQuIEFsdGhvdWdoIGl0IGlzIGtpbmQgb2Ygb2J2aW91cywgImRhdGEiLCAic2NpZW5jZSIsICJhbmFseXRpY3MiLCAiYnVzaW5lc3MiLCBhbmQgInByb2dyYW0iIGFyZSB0aGUgbW9zdCBjb21tb24gYW5kIG1vc3QgdXNlZCB3b3JkcyBpbiB0aGUgZGVncmVlIGRlc2NyaXB0aW9ucyB3aGljaCBwZXJmZWN0bHkgc3VtbWFyaXplcyB0aGUgNCBkZWdyZWUgc3ViamVjdHMgSSBkaXNjb3ZlcmVkIHRocm91Z2hvdXQgdGhlIHByb2plY3QuDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMjAyMSkNCg0Kc3VtcyA8LSBhcy5kYXRhLmZyYW1lKGNvbFN1bXMoYXMubWF0cml4KERUTSkpKQ0Kc3VtcyA8LSByb3duYW1lc190b19jb2x1bW4oc3VtcykgDQpjb2xuYW1lcyhzdW1zKSA8LSBjKCJ0ZXJtIiwgImNvdW50IikNCnN1bXMgPC0gYXJyYW5nZShzdW1zLCBkZXNjKGNvdW50KSkNCmhlYWQgPC0gc3Vtc1sxOjk5LF0NCndvcmRjbG91ZCh3b3JkcyA9IGhlYWQkdGVybSwgZnJlcSA9IGhlYWQkY291bnQsDQogIG1heC53b3Jkcz0xMDAsIHJhbmRvbS5vcmRlcj1GQUxTRSwgcm90LnBlcj0wLjM1LCANCiAgY29sb3JzPSBjKCIjRTY5RjAwIiwgIiM1NkI0RTkiLCAiIzAwOUU3MyIsICIjRjBFNDQyIiwgIiMwMDcyQjIiLCAiI0Q1NUUwMCIsICIjQ0M3OUE3IikpDQpgYGANCg0KIyBDb25jbHVzaW9uDQoNClRocm91Z2hvdXQgdGhpcyBwcm9qZWN0LCBJIHdhcyBhYmxlIHRvIGRpc2N1c3MgZGlmZmVyZW50IE1hc3RlcnMgRGVncmVlIHN1YmplY3RzIGluIGRpZmZlcmVudCB1bml2ZXJzaXRpZXMgdGhyb3VnaG91dCB0aGUgVVMuIEkgd2FzIGFibGUgdG8gc2hvdyBob3cgdGhlIGFtb3VudCBvZiBkZWdyZWVzIHRoYXQgdW5pdmVyc2l0aWVzIGhhdmUgZ3Jvd24gc2luY2UgMjAwNy4gSSB3YXMgYWxzbyBhYmxlIHRvIGNyZWF0ZSBhbiBpbnRlcmFjdGl2ZSBtYXAgdG8gdmlldyB0aGUgZGlmZmVyZW50IHNjaG9vbHMgYW5kIHRoYXQgY2FuIHRha2UgeW91IHRvIHRoZSBzY2hvb2wncyB3ZWJzaXRlIGlmIHNvbWVvbmUgd2FudGVkIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHNjaG9vbCBhbmQgaXRzIHNwZWNpZmljIGRlcGFydG1lbnQuIFRoZSBmaW5hbCB0aGluZyBJIHdhcyBhYmxlIHRvIHNob3cgd2FzIGhvdyB0byBwb3RlbnRpYWxseSBwcmVkaWN0IGZ1dHVyZSBkZWdyZWUgZGVzY3JpcHRpb25zIHVzaW5nIHRoZSBtb3N0IGNvbW1vbiB3b3JkcyBmb3VuZC4gDQoNClNpbmNlIEkgY3JlYXRlZCBteSBvd24gZGF0YXNldCwgSSB3YXMgYWJsZSB0byBpbmNsdWRlIGFsbCB0aGUgbmVjZXNzYXJ5IGluZm9ybWF0aW9uIG5lZWRlZCB0byBjb21wbGV0ZSB0aGlzIHByb2plY3QgYW5kIGRpZCBub3QgaGF2ZSBhIGxvdCBvZiBjbGVhbmluZyBhbmQgcHJlcGFyaW5nLiBNb3N0IG9mIHRoZSBjbGVhbmluZyBoYXBwZW5lZCB3aGVuIEkgY3JlYXRlZCB0aGUgTERBIG1vZGVsIHNpbmNlIHRoZSBkYXRhIG5lZWRzIHRvIGJlIHRpZHkgYW5kIG5lZWRzIHRvIGJlIG9uZS10b2tlbi1wZXItcm93IHRhYmxlIGluIG9yZGVyIHRvIGFjdHVhbGx5IHBlcmZvcm0gTERBLiANCg0KRXZlcnkgY2hhcnQgSSBwbGFubmVkIG9uIGNyZWF0aW5nIHdhcyBtYWRlIGZvciB0aGlzIHByb2plY3QuVGhlIG1vc3QgZGlmZmljdWx0IHBhcnQgb2YgdGhpcyBwcm9qZWN0IHdhcyB1c2luZyBsZWFmbGV0KCkgdG8gY3JlYXRlIG9uZSBvZiBteSBtYXBzIGFuZCBnZXR0aW5nIHRoZSBsYXllciBvcHRpb24gdG8gY29ubmVjdCB3aXRoIHRoZSBhY3R1YWwgZGF0YSBwb2ludHMuIEkgYW0gaGFwcHkgd2Ugd2VyZSBhYmxlIHRvIGZpZ3VyZSB0aGlzIG91dCBiZWNhdXNlIEkgdGhpbmsgaXQgYWxsb3dzIHBlb3BsZSB0byBsb29rIGF0IHRoaXMgaW5mb3JtYXRpb24gYW5kIGl0IG5vdCBiZSBvdmVyd2hlbG1pbmcuIFRoZSBvdGhlciBkaWZmaWN1bHQgcGFydCBJIGhhZCB3aXRoIHRoaXMgcHJvamVjdCB3YXMgTERBIFRvcGljIE1vZGVsaW5nLiBJIGtuZXcgSSB3YW50ZWQgdG8gZG8gdGhpcyBidXQgaGFkIHRyb3VibGUgYWN0dWFsbHkgdW5kZXJzdGFuZGluZyB3aGF0IGl0IHdhcyBkb2luZy4NCg0KSW4gdGVybXMgb2YgcHJpbmNpcGxlcyBvZiBkYXRhIHZpc3VhbGl6YXRpb24sIEkgdHJpZWQga2VlcGluZyBldmVyeXRoaW5nIG1pbmltYWwuIEkgd2FudGVkIGFsbCB0aGUgdmlzdWFsaXphdGlvbnMgdG8gaGF2ZSBhIGZpeGVkIHNpemUgc28gdGhhdCB0aGUgaW50ZXJhY3RpdmUgcGFydCBvZiB0aGUgcGxvdHMgd291bGQgd29yayBzbW9vdGhseSBubyBtYXR0ZXIgdGhlIHNpemUgb2YgdGhlIHNjcmVlbi4gSSBhbHNvIHVzZWQgY29sb3JzIHRoYXQgYXJlIGNvbG9yIGJsaW5kIGZyaWVuZGx5LiBMaWtlIHRoZSBwYXN0IHByb2plY3QsIEkgbWFkZSBzdXJlIHRvIGFsaWduIHRoZSB0aXRsZSBvZiB0aGUgZ3JhcGggc2tld2VkIHRvIHRoZSBsZWZ0Lg0KDQoNCg==